/*-------------------------------------------------------------------------
 *
 * attmap.c
 *	  属性映射支持。
 *
 * 此文件提供公用例程，通过比较输入和输出的 TupleDescs 来构建和管理
 * 属性映射。这种映射通常用于对继承和分区树进行的 DDL 操作
 * 进行逻辑等价的 rowtypes 之间的转换，同时考虑被删除的列。
 * 它们也被 tupconvert.c 中的元组转换例程使用。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/access/common/attmap.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/attmap.h"
#include "access/htup_details.h"
#include "utils/builtins.h"


static bool fc_check_attrmap_match(TupleDesc fc_indesc,
								TupleDesc fc_outdesc,
								AttrMap *fc_attrMap);

/*
 * make_attrmap
 *
 * 在当前内存上下文中分配属性映射的工具程序。
 */
AttrMap * make_attrmap(int fc_maplen)
{
	AttrMap    *fc_res;

	fc_res = (AttrMap *) palloc0(sizeof(AttrMap));
	fc_res->maplen = fc_maplen;
	fc_res->attnums = (AttrNumber *) palloc0(sizeof(AttrNumber) * fc_maplen);
	return fc_res;
}

/*
 * free_attrmap
 *
 * 释放属性映射的工具程序。
 */
void free_attrmap(AttrMap *fc_map)
{
	pfree(fc_map->attnums);
	pfree(fc_map);
}

/*
 * build_attrmap_by_position
 *
 * 返回一个为元组转换分配的裸属性映射，通过位置匹配输入和输出列。丢弃的列在输入和输出中都被忽略，标记为0。这通常是tupconvert.c中convert_tuples_by_position的一个子程序，但也可以独立使用。
 *
 * 注意：errdetail消息将indesc称为“返回的”行类型，将outdesc称为“预期的”行类型。这在当前用例中是可以的，但未来可能需要推广。
 */
AttrMap * build_attrmap_by_position(TupleDesc fc_indesc,
						  TupleDesc fc_outdesc,
						  const char *fc_msg)
{
	AttrMap    *fc_attrMap;
	int			fc_nincols;
	int			fc_noutcols;
	int			fc_n;
	int			fc_i;
	int			fc_j;
	bool		fc_same;

	/*
	 * 长度的计算是预期行类型的属性数量，因为它包含丢弃属性的计数。
	 */
	fc_n = fc_outdesc->natts;
	fc_attrMap = make_attrmap(fc_n);

	fc_j = 0;						/* j是下一个物理输入属性 */
	fc_nincols = fc_noutcols = 0;		/* 这些计数非丢弃属性 */
	fc_same = true;
	for (fc_i = 0; fc_i < fc_n; fc_i++)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_outdesc, fc_i);
		Oid			fc_atttypid;
		int32		fc_atttypmod;

		if (fc_att->attisdropped)
			continue;			/* attrMap->attnums[i]已经是0 */
		fc_noutcols++;
		fc_atttypid = fc_att->atttypid;
		fc_atttypmod = fc_att->atttypmod;
		for (; fc_j < fc_indesc->natts; fc_j++)
		{
			fc_att = TupleDescAttr(fc_indesc, fc_j);
			if (fc_att->attisdropped)
				continue;
			fc_nincols++;

			/* 找到匹配的列，现在检查类型 */
			if (fc_atttypid != fc_att->atttypid ||
				(fc_atttypmod != fc_att->atttypmod && fc_atttypmod >= 0))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg_internal("%s", _(fc_msg)),
						 errdetail("Returned type %s does not match expected type %s in column %d.",
								   format_type_with_typemod(fc_att->atttypid,
															fc_att->atttypmod),
								   format_type_with_typemod(fc_atttypid,
															fc_atttypmod),
								   fc_noutcols)));
			fc_attrMap->attnums[fc_i] = (AttrNumber) (fc_j + 1);
			fc_j++;
			break;
		}
		if (fc_attrMap->attnums[fc_i] == 0)
			fc_same = false;		/* 我们将在下面进行抱怨 */
	}

	/* 检查未使用的输入列 */
	for (; fc_j < fc_indesc->natts; fc_j++)
	{
		if (TupleDescAttr(fc_indesc, fc_j)->attisdropped)
			continue;
		fc_nincols++;
		fc_same = false;			/* 我们将在下面进行抱怨 */
	}

	/* 使用非丢弃列计数报告列计数不匹配 */
	if (!fc_same)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg_internal("%s", _(fc_msg)),
				 errdetail("Number of returned columns (%d) does not match "
						   "expected column count (%d).",
						   fc_nincols, fc_noutcols)));

	/* 检查映射是否具有一对一匹配 */
	if (fc_check_attrmap_match(fc_indesc, fc_outdesc, fc_attrMap))
	{
		/* 不需要运行时转换 */
		free_attrmap(fc_attrMap);
		return NULL;
	}

	return fc_attrMap;
}

/*
 * build_attrmap_by_name
 *
 * 返回一个为元组转换分配的裸属性映射，通过名称匹配输入和输出列。（丢弃的列在输入和输出中都被忽略。）这通常是tupconvert.c中convert_tuples_by_name的一个子程序，但也可以独立使用。
 */
AttrMap * build_attrmap_by_name(TupleDesc fc_indesc,
					  TupleDesc fc_outdesc)
{
	AttrMap    *fc_attrMap;
	int			fc_outnatts;
	int			fc_innatts;
	int			fc_i;
	int			fc_nextindesc = -1;

	fc_outnatts = fc_outdesc->natts;
	fc_innatts = fc_indesc->natts;

	fc_attrMap = make_attrmap(fc_outnatts);
	for (fc_i = 0; fc_i < fc_outnatts; fc_i++)
	{
		Form_pg_attribute fc_outatt = TupleDescAttr(fc_outdesc, fc_i);
		char	   *fc_attname;
		Oid			fc_atttypid;
		int32		fc_atttypmod;
		int			fc_j;

		if (fc_outatt->attisdropped)
			continue;			/* attrMap->attnums[i]已经是0 */
		fc_attname = NameStr(fc_outatt->attname);
		fc_atttypid = fc_outatt->atttypid;
		fc_atttypmod = fc_outatt->atttypmod;

		/*
		 * 现在搜索indesc中具有相同名称的属性。看来分区表的属性通常会与分区中的属性按相同顺序排列，因此下面的搜索针对这种情况进行了优化。一个关系中可能会丢弃列，但另一个中不会，因此我们使用'nextindesc'计数器来跟踪搜索的起点。如果内部循环遇到丢弃的列，那么它将不得不跳过这些列，但它应该将'nextindesc'保留在外部循环的下一个正确位置。
		 */
		for (fc_j = 0; fc_j < fc_innatts; fc_j++)
		{
			Form_pg_attribute fc_inatt;

			fc_nextindesc++;
			if (fc_nextindesc >= fc_innatts)
				fc_nextindesc = 0;

			fc_inatt = TupleDescAttr(fc_indesc, fc_nextindesc);
			if (fc_inatt->attisdropped)
				continue;
			if (strcmp(fc_attname, NameStr(fc_inatt->attname)) == 0)
			{
				/* 找到了，检查类型 */
				if (fc_atttypid != fc_inatt->atttypid || fc_atttypmod != fc_inatt->atttypmod)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("could not convert row type"),
							 errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
									   fc_attname,
									   format_type_be(fc_outdesc->tdtypeid),
									   format_type_be(fc_indesc->tdtypeid))));
				fc_attrMap->attnums[fc_i] = fc_inatt->attnum;
				break;
			}
		}
		if (fc_attrMap->attnums[fc_i] == 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("could not convert row type"),
					 errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
							   fc_attname,
							   format_type_be(fc_outdesc->tdtypeid),
							   format_type_be(fc_indesc->tdtypeid))));
	}
	return fc_attrMap;
}

/*
 * build_attrmap_by_name_if_req
 *
 * 返回由build_attrmap_by_name创建的映射，如果不需要转换则返回NULL。这是tupconvert.c中convert_tuples_by_name()和其他函数的便利例程，但也可以独立使用。
 */
AttrMap * build_attrmap_by_name_if_req(TupleDesc fc_indesc,
							 TupleDesc fc_outdesc)
{
	AttrMap    *fc_attrMap;

	/* 验证兼容性并准备属性编号映射 */
	fc_attrMap = build_attrmap_by_name(fc_indesc, fc_outdesc);

	/* 检查映射是否具有一对一匹配 */
	if (fc_check_attrmap_match(fc_indesc, fc_outdesc, fc_attrMap))
	{
		/* 不需要运行时转换 */
		free_attrmap(fc_attrMap);
		return NULL;
	}

	return fc_attrMap;
}

/*
 * check_attrmap_match
 *
 * 检查映射是否为一对一匹配，在这种情况下我们无需进行元组转换，并且属性映射不是必需的。
 */
static bool fc_check_attrmap_match(TupleDesc fc_indesc,
					TupleDesc fc_outdesc,
					AttrMap *fc_attrMap)
{
	int			fc_i;

	/* 如果属性编号不相同，则没有匹配 */
	if (fc_indesc->natts != fc_outdesc->natts)
		return false;

	for (fc_i = 0; fc_i < fc_attrMap->maplen; fc_i++)
	{
		Form_pg_attribute fc_inatt = TupleDescAttr(fc_indesc, fc_i);
		Form_pg_attribute fc_outatt = TupleDescAttr(fc_outdesc, fc_i);

		/*
		 * 如果输入列有缺失属性，我们需要进行转换。
		 */
		if (fc_inatt->atthasmissing)
			return false;

		if (fc_attrMap->attnums[fc_i] == (fc_i + 1))
			continue;

		/*
		 * 如果是一个被删除的列，且相应的输入列也被删除，
		 * 我们不需要进行转换。然而，attlen和attalign必须一致。
		 */
		if (fc_attrMap->attnums[fc_i] == 0 &&
			fc_inatt->attisdropped &&
			fc_inatt->attlen == fc_outatt->attlen &&
			fc_inatt->attalign == fc_outatt->attalign)
			continue;

		return false;
	}

	return true;
}
